#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <unordered_map>
#include <sstream>
#include <fstream>
#include <functional>

using namespace std;

static const string sep = "\r\n";
static const string headersep = ": ";
static const string blanksep = " ";
static const std::string wwwroot = "wwwroot";
static const string homepage = "1.html";
static const string httpversion = "HTTP/1.0";
static const string filesuffixsep = ".";
static const std::string args_sep = "?";

class Request;
class Response;

using func_t = function<shared_ptr<Response>(shared_ptr<Request>)>;
class Request
{
public:
    Request()
        : _blankline(sep), _path(wwwroot)
    {
    }
    ~Request()
    {
    }
    //\r\n正文
    string Getline(string &line)
    {
        if (line.empty())
            return line;
        auto pos = line.find(sep);
        if (pos == string::npos)
            return string();
        string ret = line.substr(0, pos);
        line.erase(0, pos + sep.size());
        return ret.empty() ? sep : ret;
    }
    bool ParseReqLine()
    {
        if (_req_line.empty())
            return false;
        stringstream sss(_req_line);
        sss >> _method >> _url >> _version;

        // login?user=liusong&password=dasd
        if (strcasecmp("get", _method.c_str()) == 0)
        {
            auto pos = _req_line.find(args_sep);
            if(pos != string::npos)
            {
                LOG(INFO, "change begin, url: %s\n", _url.c_str());
                _args = _req_line.substr(pos+args_sep.size());
                _url.resize(pos);
                LOG(INFO, "change finish, url: %s\n", _url.c_str());
            }
        }
        _path += _url;

        LOG(DEBUG, "url: %s\n", _url.c_str());
        if (_path[_path.size() - 1] == '/')
        {
            _path += homepage;
        }

        auto pos = _path.rfind(filesuffixsep);
        if (pos == std::string::npos)
        {
            _suffix = ".unknown";
        }
        else
        {
            _suffix = _path.substr(pos);
        }

        LOG(INFO, "client wang get %s, _suffix: %s\n", _path.c_str(), _suffix.c_str());
        return true;
    }

    bool ParseReqHeaders()
    {
        for (auto &header : _req_header)
        {
            auto pos = header.find(headersep);
            if (pos == std::string::npos)
                return false;

            string k = header.substr(0, pos);
            string v = header.substr(pos + headersep.size());
            _headers[k] = v;
        }
        return true;
    }
    bool IsExute()
    {
        return !_args.empty() || !_req_text.empty();
    }
    std::string Path()
    {
        return _path;
    }
    string Suffix()
    {
        return _suffix;
    }
    string Args()
    {
        return _args;
    }
    string Method()
    {
        return _method;
    }
    string Url()
    {
        return _url;
    }
    string Text()
    {
        return _req_text;
    }
    // 服务器————浏览器做的，我们不用写
    void Serialization()
    {
    }
    void Print()
    {
        cout << "------" << _req_line << endl;
        for (auto &header : _req_header)
        {
            cout << "*****" << header << endl;
        }
        cout << _blankline << endl;

        std::cout << "method ### " << _method << std::endl;
        std::cout << "url ### " << _url << std::endl;
        std::cout << "path ### " << _path << std::endl;
        std::cout << "httpverion ### " << _version << std::endl;
        for (auto &header : _headers)
        {
            std::cout << "@@@" << header.first << " - " << header.second << std::endl;
        }
    }
    void Deserialization(string &req)
    {
        _req_line = Getline(req);
        while (1)
        {
            string line = Getline(req);
            if (line.empty())
                break;
            else if (line == sep)
            {
                // 证明后面就是正文部分
                _req_text = req;
                break;
            }
            else
                _req_header.emplace_back(line);
        }

        ParseReqLine();
        ParseReqHeaders();
    }

private:
    // 构建原始数据
    string _req_line;
    vector<string> _req_header;
    string _blankline;
    string _req_text;

    // 需要解析出的数据
    string _method;
    string _url;
    string _path;
    string _suffix;
    string _version;
    string _args;

    unordered_map<string, string> _headers;
};

class Response
{
public:
    Response()
        : _version(httpversion), _blankline(sep)
    {
    }
    ~Response()
    {
    }

    void AddStatusLine(int code, const string &des)
    {
        _code = code;
        _code_des = des; //
    }

    void AddHeader(const string &k, const string &v)
    {
        LOG(DEBUG, "AddHeader: %s->%s\n", k.c_str(), v.c_str());
        _headers[k] = v;
    }

    void AddText(const string &text)
    {
        _res_text = text;
    }
    string Serialization()
    {
        string _status_line = _version + blanksep + to_string(_code) + blanksep + _code_des + sep;
        for (auto &header : _headers)
        {
            string tmp = header.first + headersep + header.second + sep;
            _res_header.emplace_back(tmp);
        }
        string resstr = _status_line;
        for (auto &header : _res_header)
        {
            resstr += header;
        }
        resstr += _blankline;

        resstr += _res_text;

        return resstr;
    }

    // 浏览器做的，我们不用管
    void Deserialization()
    {
    }

private:
    // 原始数据
    string _status_line;
    vector<string> _res_header;
    string _blankline;
    string _res_text;

    // 需要解析出的数据
    string _version;
    int _code;
    string _code_des;

    unordered_map<string, string> _headers;
};

class factory
{
public:
    factory()
    {
    }
    ~factory()
    {
    }

    static unique_ptr<Request> BulidRequest()
    {
        return make_unique<Request>();
    }

    static unique_ptr<Response> BulidResponse()
    {
        return make_unique<Response>();
    }
};
class HttpServer
{
public:
    HttpServer()
    {
        _mime_type.insert(std::make_pair(".html", "text/html"));
        _mime_type.insert(std::make_pair(".css", "text/css"));
        _mime_type.insert(std::make_pair(".js", "application/x-javascript"));
        _mime_type.insert(std::make_pair(".png", "image/png"));
        _mime_type.insert(std::make_pair(".jpg", "image/jpeg"));
        _mime_type.insert(std::make_pair(".unknown", "text/html"));

        _code_to_desc.insert(std::make_pair(100, "Continue"));
        _code_to_desc.insert(std::make_pair(200, "OK"));
        _code_to_desc.insert(std::make_pair(301, "Moved Permanently"));
        _code_to_desc.insert(std::make_pair(302, "Found"));
        _code_to_desc.insert(std::make_pair(404, "Not Found"));
        _code_to_desc.insert(std::make_pair(500, "Internal Server Error"));
    }

    void AddHandler(const std::string functionname, func_t f)
    {
        std::string key = wwwroot + functionname; // wwwroot/login
        _funcs[key] = f;
    }
    std::string ReadFileContent(const std::string &path, int *size)
    {
        // 要按照二进制打开
        std::ifstream in(path, ios::binary);
        if (!in.is_open())
        {
            return std::string();
        }
        in.seekg(0, in.end);
        int filesize = in.tellg();
        in.seekg(0, in.beg);

        std::string content;
        content.resize(filesize);
        in.read((char *)content.c_str(), filesize);
        in.close();
        *size = filesize;
        return content;
    }
    string HandleHttpRequest(string req)
    {
#ifdef TEST
        cout << "——————————————————————————————" << endl;
        cout << req;
        string response = "HTTP/1.0 200 OK\r\n";
        response += "\r\n";
        response += "<html><body><h1> hello liusong! </h1></body</html>";
        return response;
#else
        shared_ptr<Request> request = factory::BulidRequest();
        request->Deserialization(req);
        request->Print();

        shared_ptr<Response> response = factory::BulidResponse();

        int code = 0;
        // string newurl = "https://www.qq.com/";
        // string newurl = "https://www.baidu.com/\r\n";
        // if (request->Path() == "wwwroot/redir")
        // {
        //     code = 301;
        //     response->AddStatusLine(code, _code_to_desc[code]);
        //     response->AddHeader("Location", newurl);
        //     LOG(INFO, "newurl: %s", newurl);
        //     LOG(INFO, "path : %s", request->Path().c_str());
        // }

        /// login?user=liusong&password=dasd
        if (request->IsExute())
        {
            response = _funcs[request->Path()](request);
            return response->Serialization();
        }
        else
        {
            code = 200;
            int contentsize = 0;
            string text = ReadFileContent(request->Path(), &contentsize);
            if (text.empty())
            {
                code = 404;
                response->AddStatusLine(code, _code_to_desc[code]);
                string text404 = ReadFileContent("wwwroot/404.html", &contentsize);
                response->AddHeader("Content-Length", std::to_string(contentsize));
                response->AddHeader("Content-Type", _mime_type[".html"]);
                response->AddText(text404);
            }
            else
            {
                response->AddHeader("Content-length", to_string(contentsize));
                response->AddText(text);
                LOG(INFO, "success, path : %s", request->Path().c_str());

                string suffix = request->Suffix();
                response->AddHeader("Content-Type", _mime_type[suffix]);
            }
        }

        return response->Serialization();
#endif
    }
    ~HttpServer()
    {
    }

private:
    unordered_map<string, string> _mime_type;
    unordered_map<int, string> _code_to_desc;

    unordered_map<string,func_t> _funcs;
};